package li.kaeppe.travel.tracker.dao;

import java.math.BigDecimal;
import java.util.List;

import li.kaeppe.travel.tracker.domain.Trip;
import li.kaeppe.travel.tracker.domain.User;
import li.kaeppe.travel.tracker.service.TripIdCreator;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.transaction.annotation.Transactional;

@Transactional
public class HibernateTripDaoImpl implements TripDao {

	/* **************************************************** */
	/*                      CONSTANTS                       */
	/* **************************************************** */

	private static final Log log = LogFactory
			.getLog(HibernateTripDaoImpl.class);

	/* **************************************************** */
	/*                     MEMBER FIELDS                    */
	/* **************************************************** */
	
	private SessionFactory sessionFactory;
	
	private TripIdCreator uuidGenerator;
	
	/* **************************************************** */
	/*                      CONSTRUCTOR                     */
	/* **************************************************** */

	/* **************************************************** */
	/*                    PUBLIC METHODS                    */
	/* **************************************************** */

	public void deleteTrip(Trip trip) {
		if(log.isDebugEnabled()) {
			log.debug("HibernateTripDaoImpl -> deleteTrip()");
		}
		
		this.getSession().delete(trip);
	}

	@SuppressWarnings("unchecked")
	public List<Trip> getAllTrips() {
		if(log.isDebugEnabled()) {
			log.debug("HibernateTripDaoImpl -> getAllTrips()");
		}
		
		List<Trip> trips = this.getSession().createQuery("from Trip").list();
		return trips;
	}

	@SuppressWarnings("unchecked")
	public List<Trip> getAllTrips(User user) {
		if(log.isDebugEnabled()) {
			log.debug("HibernateTripDaoImpl -> getAllTrips()");
		}
		
		List<Trip> trips = this.getSession().createQuery("from Trip t where t.user = :userId").
											 setLong("userId", user.getId()).
											 list();
		return trips;
	}
	
	public Trip storeTrip(Trip trip) {
//		if(trip.getKey() == null) {
//			trip.setKey(this.getUuidGenerator().generateTripId(trip));
//		}
		if(log.isDebugEnabled()) {
			log.debug("HibernateTripDaoImpl -> storeTrip() with key [" +
						trip.getKey() + "]");
		}
		this.getSession().saveOrUpdate(trip);
		return trip;
	}
	
	public Trip getTripById(long id) {
		if(log.isDebugEnabled()) {
			log.debug("HibernateTripDaoImpl -> getTrip()");
		}
		Query query = this.getSession().createQuery("from Trip t where t.id = :id");
		query.setLong("id", id);
		return (Trip)query.uniqueResult();
	}
	
	public Trip getTripByKey(String key, String user) {
		if(log.isDebugEnabled()) {
			log.debug("HibernateTripDaoImpl -> getTripByKey()");
		}
		Query query = this.getSession().createQuery(
						"from Trip t where t.key = :key and t.user.username = :username"
					  );
		query.setString("key", key);
		query.setString("username", user);
		return (Trip)query.uniqueResult();	
	}

	public void updateCalculatedTripData(Trip trip) {
		
		if(trip.getWayPoints().size() > 0 ) {
			Query query = this.getSession().createQuery(
							 "select min(p.latitude) as minLat, max(p.latitude) as maxLat, " +
							 "       min(p.longitude) as minLong, max(p.longitude) as maxLong " +
							 "from WayPoint p " +
							 "where p.trip.id = :tripId"
						   );
			query.setLong("tripId", trip.getId());
			Object[] obj = (Object[])query.uniqueResult();
			
			trip.setMinLatitude((BigDecimal)obj[0]);
			trip.setMaxLatitude((BigDecimal)obj[1]);
			trip.setMinLongitude((BigDecimal)obj[2]);
			trip.setMaxLongitude((BigDecimal)obj[3]);
		}
		
	}
	
	/* **************************************************** */
	/*                  PROTECTED METHODS                   */
	/* **************************************************** */

	/* **************************************************** */
	/*                   PRIVATE METHODS                    */
	/* **************************************************** */

	private Session getSession() {
		return this.getSessionFactory().getCurrentSession();
	}
	
	/* **************************************************** */
	/*                  GETTERS AND SETTERS                 */
	/* **************************************************** */
	
	public SessionFactory getSessionFactory() {
		return sessionFactory;
	}

	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}
	
	public TripIdCreator getUuidGenerator() {
		return uuidGenerator;
	}

	public void setUuidGenerator(TripIdCreator uuidGenerator) {
		this.uuidGenerator = uuidGenerator;
	}
}
